﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO.MemoryMappedFiles;

namespace UbwApplication
{
    public class mIRCMessenger
    {
        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
        [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
        public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
        [DllImport("User32.dll", SetLastError=true)]
        public static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int lParam);

        public const int WM_USER = 0x0400;
        public const int WM_MCOMMAND = WM_USER + 200;
        public const int WM_MEVALUATE = WM_USER + 201;

        internal IntPtr _windowHandle;
        public int WindowHandle
        {
            get
            {
                return _windowHandle.ToInt32();
            }
        }

        internal int memoryFileSize = 4096;
        internal int memoryFileIndex = 1;
        internal MemoryMappedFile memoryFile;
        internal string memoryFileName
        {
            get 
            {
                if (memoryFileIndex == 0)
                    return "mIRC";
                else
                    return "mIRC" + memoryFileIndex.ToString();
            }
        }

        public mIRCMessenger()
        {
            memoryFile = MemoryMappedFile.CreateNew(memoryFileName, memoryFileSize, MemoryMappedFileAccess.ReadWrite);
            _windowHandle = IntPtr.Zero;
            UpdatemIRCWindowHandle();
        }

        public void UpdatemIRCWindowHandle()
        {
            IntPtr mainHandle = FindWindow("mIRC", null);
            IntPtr serverHandle = FindWindowEx(mainHandle, new IntPtr(0), "MDIClient", null);

            _windowHandle = mainHandle;
        }

        public string Evaluate(string expression)
        {
            if (_windowHandle.Equals(IntPtr.Zero)) throw new InvalidOperationException("No mIRC window set");

            setParameterString(expression);

            int result = SendMessage(_windowHandle, WM_MEVALUATE, 0, memoryFileIndex);
            if (result == 0)
                throw new SendMessageException(string.Format("WM_MEVALUATE to {0} failed: {1}", WindowHandle, Marshal.GetLastWin32Error()));

            return getReturnedString();
        }

        public void Command(string command)
        {
            if (_windowHandle.Equals(IntPtr.Zero)) return;

            setParameterString(command);

            int result = SendMessage(_windowHandle, WM_MCOMMAND, 1, memoryFileIndex);
            if (result == 0)
                throw new SendMessageException(string.Format("WM_MCOMMAND to {0} failed: {1}", WindowHandle, Marshal.GetLastWin32Error()));
        }

        internal void setParameterString(string param)
        {
            using (var mmva = memoryFile.CreateViewStream(0, 0))
            {
                // clear the file (write all zeros)
                mmva.Write((byte[])Array.CreateInstance(typeof(byte), memoryFileSize), 0, memoryFileSize);
                mmva.Seek(0, System.IO.SeekOrigin.Begin);

                // write our new value
                using (var ms = new System.IO.StreamWriter(mmva, ASCIIEncoding.ASCII))
                {
                    ms.Write(param);
                }
            }

        }

        internal string getReturnedString()
        {
            string response = "";

            using (var mmva = memoryFile.CreateViewStream(0, 0))
            {
                using (var ms = new System.IO.StreamReader(mmva, ASCIIEncoding.ASCII))
                {
                    response = ms.ReadToEnd();
                    int nullIdx = response.IndexOf('\0');
                    response = response.Substring(0, nullIdx);
                }
            }

            return response;
        }
    }

    [Serializable]
    public class SendMessageException : Exception
    {
        public SendMessageException() { }
        public SendMessageException(string message) : base(message) { }
        public SendMessageException(string message, Exception inner) : base(message, inner) { }
        protected SendMessageException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }
}
